package cn.com.connext.scrm.config;

import cn.com.connext.scrm.utils.ProfileUtil;
import com.connext.common.security.AuthoritiesConstants;
import com.connext.common.security.OAuth2JwtTokenStore;
import com.connext.common.security.SpringSecurityAuditorAware;
import io.github.jhipster.config.JHipsterProperties;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.web.client.RestTemplate;

import javax.cache.CacheManager;
import java.util.Map;

//@Configuration
//@EnableResourceServer
//@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MicroserviceSecurityConfiguration extends ResourceServerConfigurerAdapter {

    private final JHipsterProperties jHipsterProperties;

    private final Environment env;

    private final DiscoveryClient discoveryClient;

    public MicroserviceSecurityConfiguration(JHipsterProperties jHipsterProperties,
                                             Environment env, DiscoveryClient discoveryClient) {

        this.jHipsterProperties = jHipsterProperties;
        this.env = env;
        this.discoveryClient = discoveryClient;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        if (ProfileUtil.haveProfile(env, "prod")) {
            configureForProd(http);
        } else {
            configureForDev(http);
        }
    }

    /**
     * 针对开发环境的安全策略
     *
     * @param http
     * @throws Exception
     */
    private void configureForDev(HttpSecurity http) throws Exception {
        http
            .csrf()
            .disable()
            .headers()
            .frameOptions()
            .disable()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/api/profile-info").permitAll()
            .antMatchers("/api/**").permitAll()
            .antMatchers("/api/activity/draw/**").permitAll()
            .antMatchers("/api/wxBusifavorCallbacks/**").permitAll()
            .antMatchers("/management/health").permitAll()
            .antMatchers("/management/**").permitAll()
            .antMatchers("/swagger-resources/configuration/ui").permitAll();
    }

    /**
     * 针对生成环境的安全策略
     *
     * @param http
     * @throws Exception
     */
    private void configureForProd(HttpSecurity http) throws Exception {
        http
            .csrf()
            .disable()
            .headers()
            .frameOptions()
            .disable()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/api/profile-info").permitAll()
//            .antMatchers("/api/**").authenticated()
            .antMatchers("/api/activity/draw/**").permitAll()
            .antMatchers("/api/wxMerchantRoll/**").permitAll()
            .antMatchers("/api/**").authenticated()
            .antMatchers("/management/health").permitAll()
            .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
            .antMatchers("/swagger-resources/configuration/ui").permitAll();
    }

    @Bean
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter, RestTemplate keyUriRestTemplate, CacheManager cacheManager) {
        if (ProfileUtil.haveProfile(env, "no-uaa")) {
            return null;
        } else {
            return new OAuth2JwtTokenStore(jwtAccessTokenConverter, keyUriRestTemplate, cacheManager);
        }

    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(
        @Qualifier("loadBalancedRestTemplate") RestTemplate keyUriRestTemplate) {

        if (ProfileUtil.haveProfile(env, "no-uaa")) {
            return null;
        } else {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setVerifierKey(getKeyFromAuthorizationServer(keyUriRestTemplate));
            return converter;
        }
    }

    @Bean
    public RestTemplate loadBalancedRestTemplate(RestTemplateCustomizer customizer) {
        if (ProfileUtil.haveProfile(env, "no-uaa")) {
            return null;
        } else {
            RestTemplate restTemplate = new RestTemplate();
            customizer.customize(restTemplate);
            return restTemplate;
        }
    }

    @Bean
    public SpringSecurityAuditorAware springSecurityAuditorAware() {
        return new SpringSecurityAuditorAware();
    }

    private String getKeyFromAuthorizationServer(RestTemplate keyUriRestTemplate) {
        // Load available UAA servers
        discoveryClient.getServices();
        HttpEntity<Void> request = new HttpEntity<Void>(new HttpHeaders());
        return (String) keyUriRestTemplate
            .exchange("http://uaa/oauth/token_key", HttpMethod.GET, request, Map.class).getBody()
            .get("value");

    }
}
